package com.htdst.mockserver.tcpserver;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import org.apache.log4j.Logger;

import com.htdst.mockserver.util.Config;

public abstract class SocketHandler implements Runnable, Handler
{
	private static final Logger	logger			= Logger.getLogger(SocketHandler.class);
	private Socket				socket			= null;
	private String				charsetEncoding	= null;

	SocketHandler(Socket socket)
	{
		this.socket = socket;
	}

	SocketHandler(Socket socket, String charsetEncoding)
	{
		this(socket);
		this.charsetEncoding = charsetEncoding;
	}

	@Override
	public abstract String handle(String request);

	@Override
	public void run()
	{
		DataInputStream dis = null;
		DataOutputStream dos = null;
		try
		{
			logger.debug(new StringBuilder().append("Receive from: ")
					.append(getRemoteAddress()).append(" : ")
					.append(socket.getPort()).toString());

			String input = read(dis);
			logger.debug("Input String As: ");
			logger.debug("'" + input + "'");

			// handle the request
			String output = handle(input);

			// write the output
			write(dos, output);

			logger.debug("Output String As: ");
			logger.debug("'" + output + "'");

		}
		catch (Throwable t)
		{
			logger.error("Error occured...", t);
		}
		finally
		{
			if (dis != null)
			{
				try
				{
					dis.close();
				}
				catch (IOException e)
				{
					logger.error("Error occurred when close input stream.", e);
				}
			}
			if (dos != null)
			{
				try
				{
					dos.close();
				}
				catch (IOException e)
				{
					logger.error("Error occured when close writer.", e);
				}
			}
			if (socket != null)
			{
				try
				{
					socket.close();
				}
				catch (IOException e)
				{
					logger.error("Error occured when close the socket.", e);
				}
			}
		}
	}

	private String getRemoteAddress()
	{
		InetAddress remoteAddress = socket.getInetAddress();
		if (null == remoteAddress)
		{
			logger.warn("Can not get ip address");
			return null;
		}
		return remoteAddress.getHostAddress();
	}

	private String read(DataInputStream dis) throws IOException
	{
		dis = new DataInputStream(new BufferedInputStream(
				socket.getInputStream()));
		final int requestLength = dis.readInt();
		byte[] request = new byte[requestLength];
		dis.readFully(request);
		return new String(request, getCharsetEncoding());
	}

	private void write(DataOutputStream dos, String output) throws IOException
	{
		dos = new DataOutputStream(new BufferedOutputStream(
				socket.getOutputStream()));
		dos.writeInt(output.length());
		dos.write(output.getBytes(getCharsetEncoding()));
		dos.flush();
	}

	public String getCharsetEncoding()
	{
		return charsetEncoding == null ? Config.getCharsetEncoding()
				: charsetEncoding;
	}
}
